package com.baiwei.vpnapi.service.impl;

import com.baiwei.vpnapi.dao.DeviceMapper;
import com.baiwei.vpnapi.dao.UserMapper;
import com.baiwei.vpnapi.dao.UserPackageMapper;
import com.baiwei.vpnapi.entity.VpnDevice;
import com.baiwei.vpnapi.entity.VpnUser;
import com.baiwei.vpnapi.entity.reqentity.LoginInput;
import com.baiwei.vpnapi.entity.respentity.AvailablePackage;
import com.baiwei.vpnapi.exception.AccountException;
import com.baiwei.vpnapi.exception.AuthException;
import com.baiwei.vpnapi.exception.BusinessEnum;
import com.baiwei.vpnapi.exception.VpnException;
import com.baiwei.vpnapi.service.AuthService;
import com.baiwei.vpnapi.utils.AppUtils;
import com.baiwei.vpnapi.utils.JwtTokenUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.util.internal.StringUtil;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @program: vpnapi
 * @description: 用户认证实现
 * @author: Mr. Stone
 * @create: 2020-02-24 18:05
 **/

@Service(value="vpnAuthService")
public class AuthServiceImpl implements AuthService {
    @Value("${smsconfig.url}")
    private String smsUrl;

    @Value("${smsconfig.account}")
    private String smsAccount;

    @Value("${smsconfig.apikey}")
    private String smsApikey;

    @Value("${smsconfig.sender}")
    private String smsSender;

    @Value("${smsconfig.template}")
    private String smsTpl;

    @Value("${ponymail.account}")
    private String mailAccount;

    @Value("${ponymail.password}")
    private String mailPwd;

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private VpnServiceImpl vpnService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private UserPackageMapper userPackageMapper;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private static Logger logger = LoggerFactory.getLogger(AuthServiceImpl.class);

    /**
    * @Description: 登录
    * @Param: 
    * @return: 
    * @Author: Mr.Stone
    * @Date: 2020/3/21
    */
    @Override
    public Object login(LoginInput input) {
        input.validation();
        VpnUser quser = new VpnUser();
        quser.setUsername(input.getUsername());
        VpnUser cuser = userMapper.selectOne(new QueryWrapper<>(quser));
        logger.info("user login: {}", cuser);
        if(cuser == null) {
            throw new AccountException(BusinessEnum.USER_LOGIN_NOT_EXIST);
        }
        if(cuser.getStatus().equals(0)) {
            throw new AccountException(BusinessEnum.USER_IN_BLACK_LIST);
        }
        //cuser.setRegVersion(request.getHeader("version"));
        //cuser.setCurPlatform(request.getHeader("platform"));
        logger.info("login param version {}", request.getHeader("version"));
        String ipAddr = AppUtils.getIpAddr(request);
        cuser.setLoginPlatform(request.getHeader("platform"));

        cuser.setLoginVersion(request.getHeader("version"));
        cuser.setLoginIp(ipAddr);
        userMapper.updateById(cuser);
        String password = cuser.getPassword();
        if(password == null || password.length() == 0) {
            throw new AuthException(BusinessEnum.AUTH_USER_UPDATE_WARNING);
        }
        if(!cuser.getPassword().equals(input.getPassword())) {
            throw new AccountException(BusinessEnum.PASSWORD_LOGIN_WRONG);
        }
        String device_identity = request.getHeader("device");
        if(device_identity == null || device_identity.length() == 0) {
            device_identity = UUID.randomUUID().toString();
            VpnDevice device = new VpnDevice();
            device.setIdentifier(device_identity);
            logger.info("insert device info {}", device);
            deviceMapper.insert(device);
        }
        String idString = cuser.getUserId().toString();
        List<AvailablePackage> exclusivePackage = userPackageMapper.findExclusivePackage(cuser.getUserId());
        HashMap<String, Object> userInfo = new HashMap<>();
        if(exclusivePackage != null && exclusivePackage.size() > 0) {
            List<String> ss = exclusivePackage.stream()
                    .map(e -> e.getServerId().toString())
                    .collect(Collectors.toList());
            String user_server = StringUtil.join(",", ss).toString();
            userInfo.put("server", user_server);
        }

        String token = jwtTokenUtil.doGenerateToken(userInfo, idString);
        Map<String, Object> res = new HashMap<>();
        res.put("token", token);
        res.put("device", device_identity);
        if(logger.isInfoEnabled()) {
            logger.info(res.toString());
        }
        return res;
    }

    @Override
    public Object logout() {
        String device = request.getHeader("device");
        String token = request.getHeader("Authorization");
        String userIdString = jwtTokenUtil.getUsernameFromToken(token);
        if(device == null || device.length() == 0) {
            throw new VpnException(BusinessEnum.VPN_CONFIG_DEVICE_EMPTY);
        }
        vpnService.disconnect(device, userIdString);
        return "success";
    }

   /**
   * @Description: 短信发送验证码
   * @Param: 
   * @return: 
   * @Author: Mr.Wang
   * @Date: 2020/3/21
   */
    @Override
    public Object sendAuthCodeSms(String phone) {
        String authCode = AppUtils.generateAuthCode();
        String smsContent = String.format(smsTpl, authCode);
        stringRedisTemplate.opsForValue().set(phone, authCode, 5, TimeUnit.MINUTES);
        RestTemplate rest = new RestTemplate();
        String url = smsUrl + "?"
                + "account_name=" + smsAccount + "&"
                + "api_key=" + smsApikey + "&"
                + "sender=" + smsSender + "&"
                + "mobile=" + phone + "&"
                + "sms_content=" + smsContent;
        String result = rest.getForObject(url, String.class);
        ObjectMapper mapper = new ObjectMapper();
        try {
            Map<String, Object> res = mapper.readValue(result, Map.class);
            Integer sta = (Integer)(res.get("status"));
            if(sta == 1) {
                return "success";
            } else {
                throw new RuntimeException("验证码发送错误");
            }
        } catch (JsonParseException e) {
            throw new RuntimeException("验证码发送错误");
        } catch (JsonMappingException me) {
            throw new RuntimeException("验证码发送错误");
        } catch (IOException ie) {
            throw new RuntimeException("验证码发送错误");
        }
    }

    /**
    * @Description: 发送邮箱验证码
    * @Param:
    * @return:
    * @Author: Mr.Wang
    * @Date: 2020/3/21
    */
    @Override
    public Object sendAuthCodeEmail(String emailAddr) {
        System.out.println(AppUtils.getIpAddr(request));
        String authCode = AppUtils.generateAuthCode();
        stringRedisTemplate.opsForValue().set(emailAddr, authCode, 5, TimeUnit.MINUTES);

        try {
            HtmlEmail email = new HtmlEmail();
            email.setHostName("smtp.gmail.com");
            email.setCharset("utf-8");
            email.addTo(emailAddr);
            email.setFrom(mailAccount,"小马VPN");
            email.setAuthentication(mailAccount,
                    mailPwd);
            email.setSubject("重置密码验证");
            email.setMsg("您正在进行重置密码，验证码："+authCode+"。验证码有效时间五分钟");
            email.setSSLOnConnect(true);
            logger.info("authCode is {}", authCode);
            logger.info("send mail from {} pwd {} to {}", mailAccount, mailPwd, emailAddr);
            email.send();
        } catch (EmailException e) {
            throw new RuntimeException("发送邮件异常:" + e.getMessage());
        }
        return "success";
    }

    /**
    * @Description: 验证码校验
    * @Param:
    * @return:
    * @Author: Mr.Wang
    * @Date: 2020/3/21
    */
    @Transactional
    public Object codeAuth(String username, String password, String authCode) {
        String rAuthToken = stringRedisTemplate.opsForValue().get(username);
        if(rAuthToken == null) {
            throw new AuthException(BusinessEnum.AUTH_CODE_EXPIRE);
        }
        if(!rAuthToken.equals(authCode)) {
            throw new AuthException(BusinessEnum.AUTH_CODE_NOT_MATCH);
        }
        VpnUser quser = new VpnUser();
        quser.setUsername(username);
        VpnUser user = userMapper.selectOne(new QueryWrapper<>(quser));
        if(user == null) {
            throw new AuthException(BusinessEnum.USER_LOGIN_NOT_EXIST);
        }
        user.setPassword(password);
        userMapper.updateById(user);
        return "success";
    }

    @Override
    public Object getMessage(Integer pageNo, Integer pageSize) {

        return null;
    }


    public String matchAuthCode(String authCode, String username) {
        String rAuthToken = stringRedisTemplate.opsForValue().get(username);
        if(rAuthToken == null) {
            throw new AuthException(BusinessEnum.AUTH_CODE_EXPIRE);
        }
        if(!rAuthToken.equals(authCode)) {
            throw new AuthException(BusinessEnum.AUTH_CODE_NOT_MATCH);
        }
        return "success";
    }
}
